use std::panic;

use algorithms_fourth::red_black_search::trace::{self, TraceRedBlackBST};
use algorithms_fourth::red_black_search::RedBlackBST;
use algorithms_fourth::search::ST;
use algorithms_fourth::string::huff_man::{HuffmanDecode, HuffmanEncode};
// This is the interface to the JVM that we'll call the majority of our
// methods on.
use jni::JNIEnv;

// These objects are what you should use as arguments to your native
// function. They carry extra lifetime information to prevent them escaping
// this context and getting used after being GC'd.
use jni::objects::{JClass, JObject, JPrimitiveArray, JString, ReleaseMode};

// This is just a pointer. We'll be returning it from our function. We
// can't return one of the objects with lifetime information because the
// lifetime checker won't let us.

use jni::sys::{jarray, jboolean, jint, jlong, jstring};
use log::LevelFilter;

///
#[no_mangle]
pub extern "system" fn Java_com_example_result_Algorithm_markdownToHtml<'local>(
    mut env: JNIEnv<'local>,
    _class: JClass<'local>,
    data: JString<'local>,
) -> jstring {
    let data: String = env.get_string(&data).unwrap().into();
    let ans = markdown::to_html_with_options(&data, &markdown::Options::gfm()).unwrap();
    env.new_string(ans).unwrap().into_raw()
}

///重定向崩溃信息
#[no_mangle]
pub extern "system" fn Java_com_example_result_Algorithm_setPanic<'local>(
    mut _env: JNIEnv<'local>,
    _class: JClass<'local>,
) {
    panic::set_hook(Box::new(|info| log::error!("{}", info)))
}
type SiRedBlackBST = TraceRedBlackBST<String, i32>;
#[no_mangle]
pub extern "system" fn Java_com_example_result_Algorithm_createRedBlackST<'local>(
    mut _env: JNIEnv<'local>,
    _class: JClass<'local>,
) -> jlong {
    android_logger::init_once(android_logger::Config::default().with_max_level(LevelFilter::Trace));

    let red_black: SiRedBlackBST = trace::new(RedBlackBST::new(), Vec::new());
    let p = Box::into_raw(Box::new(red_black));
    // let a= p as usize;
    // log::info!("Java_com_example_result_Algorithm_createRedBlackST:{:?} {:?} ", p,a);
    p as jlong
}
#[no_mangle]
pub extern "system" fn Java_com_example_result_Algorithm_closeRedBlackST<'local>(
    mut _env: JNIEnv<'local>,
    _class: JClass<'local>,
    red_black_st: jlong,
) {
    log::info!("closeRedBlackST:{:?}", red_black_st as *mut SiRedBlackBST);
    let _red_black_search = unsafe { Box::from_raw(red_black_st as *mut SiRedBlackBST) };
}

/// 打印红黑树的操作图
/// # 注意
/// 因为是以string的方式输出，所以当操作过多，节点过多时，可能会内存溢出，建议控制在20个节点20个操作内
#[no_mangle]
pub extern "system" fn Java_com_example_result_Algorithm_printRedBlackST<'local>(
    env: JNIEnv<'local>,
    _class: JClass<'local>,
    red_black_st: jlong,
) -> jstring {
    log::info!("Java_com_example_result_Algorithm_printRedBlackST");
    let red_black_search = unsafe { &mut *(red_black_st as *mut SiRedBlackBST) };
    let graph = red_black_search.get_graph();
    env.new_string(graph)
        .expect("转换String为jstring失败")
        .into_raw()
}
#[no_mangle]
pub extern "system" fn Java_com_example_result_Algorithm_inertOrUpdateNodeToRedBlackST<'local>(
    mut env: JNIEnv<'local>,
    _class: JClass<'local>,
    red_black_st: jlong,
    k: JString<'local>,
    v: jint,
) {
    log::info!("Java_com_example_result_Algorithm_inertOrUpdateNodeToRedBlackST");
    let red_black_search = unsafe { &mut *(red_black_st as *mut SiRedBlackBST) };
    red_black_search.put(env.get_string(&k).unwrap().into(), v);
}
/// 删除红黑树中的节点
#[no_mangle]
pub extern "system" fn Java_com_example_result_Algorithm_deleteNodeToRedBlackST<'local>(
    mut env: JNIEnv<'local>,
    _class: JClass<'local>,
    red_black_st: jlong,
    k: JString<'local>,
) {
    let red_black_search = unsafe { &mut *(red_black_st as *mut SiRedBlackBST) };
    red_black_search.delete(&env.get_string(&k).unwrap().into());
}
///获取红黑树中的最大节点键值
#[no_mangle]
pub extern "system" fn Java_com_example_result_Algorithm_maxNodeToRedBlackST<'local>(
    env: JNIEnv<'local>,
    _class: JClass<'local>,
    red_black_st: jlong,
) -> jstring {
    let red_black_search = unsafe { &mut *(red_black_st as *mut SiRedBlackBST) };
    let ans = red_black_search.max();
    if let Some(ans) = ans {
        env.new_string(ans).unwrap().into_raw()
    } else {
        JObject::null().into_raw()
    }
}

/// 获取红黑树节点中的最小节点键值
#[no_mangle]
pub extern "system" fn Java_com_example_result_Algorithm_minNodeToRedBlackST<'local>(
    env: JNIEnv<'local>,
    _class: JClass<'local>,
    red_black_st: jlong,
) -> jstring {
    let red_black_search = unsafe { &mut *(red_black_st as *mut SiRedBlackBST) };
    let min = red_black_search.min();
    if let Some(min) = min {
        env.new_string(min).unwrap().into_raw()
    } else {
        JObject::null().into_raw()
    }
}
/// 键值是否存在
#[no_mangle]
pub extern "system" fn Java_com_example_result_Algorithm_hasNodeToRedBlackST<'local>(
    mut env: JNIEnv<'local>,
    _class: JClass<'local>,
    red_black_st: jlong,
    k: JString<'local>,
) -> jboolean {
    let red_black_search = unsafe { &mut *(red_black_st as *mut SiRedBlackBST) };
    let k = env.get_string(&k).unwrap().into();
    red_black_search.contains(&k) as jboolean
}
/// 获取红黑树中对应键的值
/// ## panic
/// 如果键值不存
#[no_mangle]
pub extern "system" fn Java_com_example_result_Algorithm_getValueToRedBlackST<'local>(
    mut env: JNIEnv<'local>,
    _class: JClass<'local>,
    red_black_st: jlong,
    k: JString<'local>,
) -> jint {
    let red_black_search = unsafe { &mut *(red_black_st as *mut SiRedBlackBST) };
    let k = env.get_string(&k).unwrap().into();
    let v = red_black_search.get(&k);
    *v.unwrap()
}
#[no_mangle]
pub extern "system" fn Java_com_example_result_Algorithm_huffmanDecode<'local>(
    mut env: JNIEnv<'local>,
    _class: JClass<'local>,
    data: JPrimitiveArray<'local, i8>,
) -> jstring {
    let data = unsafe {
        env.get_array_elements(&data, ReleaseMode::NoCopyBack)
            .expect("")
    };
    let data = data.iter().map(|f| *f as u8).collect::<Vec<_>>();
    let data = HuffmanDecode::new(data).expand();
    env.new_string(data).expect("新建String失败！").into_raw()
}

#[no_mangle]
pub extern "system" fn Java_com_example_result_Algorithm_huffmanEncode<'local>(
    mut env: JNIEnv<'local>,
    _class: JClass<'local>,
    data: JString<'local>,
) -> jarray {
    let input: String = env.get_string(&data).expect("不能得到数据").into();
    let data = HuffmanEncode::new().compress(&input);
    let data = env
        .byte_array_from_slice(&data)
        .expect("创建ByteArray失败！");
    data.into_raw()
}
// This keeps Rust from "mangling" the name and making it unique for this
// crate.
#[no_mangle]
pub extern "system" fn Java_com_example_result_Algorithm_hello<'local>(
    mut env: JNIEnv<'local>,
    // This is the class that owns our static method. It's not going to be used,
    // but still must be present to match the expected signature of a static
    // native method.
    class: JClass<'local>,
    input: JString<'local>,
) -> jstring {
    // First, we have to get the string out of Java. Check out the `strings`
    // module for more info on how this works.
    let input: String = env
        .get_string(&input)
        .expect("Couldn't get java string!")
        .into();
    let data_id = env
        .get_static_field("com/example/result/Algorithm", "id", "I")
        .expect("id 获取错误")
        .i()
        .expect("id 类型错误");
    // Then we have to create a new Java string to return. Again, more info
    // in the `strings` module.
    let output = env
        .new_string(format!("Hello{:?}, {}!\n{:?}", data_id, input, class))
        .expect("Couldn't create java string!");

    // Finally, extract the raw pointer to return.
    output.into_raw()
}
